/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using STSdb.Files;

namespace STSdb.Data
{
    public partial class FTable<TKey, TRecord>
    {
        internal bool Split = false;

        internal class Link
        {
            private LinkedListNode<Link> Node;

            public readonly FTable<TKey, TRecord> BufferManager;
            public Link Parent;
            public readonly LinkWrapper Wrapper;
            public BaseCollection Collection;

            public bool IsModified;

            public Link(FTable<TKey, TRecord> bufferManager, Link parent, LinkWrapper wrapper)
            {
                Debug.Assert(bufferManager != null);
                Debug.Assert(wrapper != null);

                Parent = parent;
                BufferManager = bufferManager;
                Wrapper = wrapper;
                Wrapper.Link = this;
                IsModified = false;
                if (wrapper.Ptr.IsNull)
                    Collection = new CollapsedCollection(this);

                if (Parent == null)
                    Node = BufferManager.Cache.AddFirst(this);
                else
                    Node = BufferManager.Cache.AddAfter(Parent.Node, this);

                BufferManager.ClearOverflow();
            }

            public void __Link()
            {
                BufferManager.Cache.Remove(Node);
                Wrapper.Link = null;
            }

            public void Touch()
            {
                BufferManager.Cache.Remove(Node);
                if (Parent == null)
                    BufferManager.Cache.AddFirst(Node);
                else
                    BufferManager.Cache.AddAfter(Parent.Node, Node);
            }

            public void Save()
            {
                if (!IsModified)
                    return;

                if (Parent != null)
                    Parent.IsModified = true;

                using (MemoryStream stream = new MemoryStream())
                {
                    BinaryWriter writer = new BinaryWriter(stream);
                    bool isSystem = Collection is ExpandedCollection;
                    writer.Write(isSystem);

                    Collection.Save(stream);

                    ulong size = Wrapper.Ptr.Size;
                    if (stream.Length == 1)
                        Wrapper.Ptr = Ptr.NULL;
                    else
                    {
                        Wrapper.Ptr = new Ptr(BufferManager.RawFile.Length, (ulong)stream.Length);
                        byte[] buffer = stream.GetBuffer();
                        BufferManager.RawFile.Write(Wrapper.Ptr.Position, buffer, 0, (int)Wrapper.Ptr.Size);
                    }

                    if (isSystem)
                        BufferManager.SystemSize += Wrapper.Ptr.Size - size;
                    else
                        BufferManager.DataSize += Wrapper.Ptr.Size - size;
                }

                IsModified = false;
            }

            public void Load()
            {
                Debug.Assert(!Wrapper.Ptr.IsNull);

                byte[] buffer = new byte[Wrapper.Ptr.Size];
                BufferManager.RawFile.Read(Wrapper.Ptr.Position, buffer, 0, (int)Wrapper.Ptr.Size);
                using (MemoryStream stream = new MemoryStream(buffer))
                {
                    BinaryReader reader = new BinaryReader(stream);
                    if (reader.ReadBoolean())
                        Collection = new ExpandedCollection(this);
                    else
                        Collection = new CollapsedCollection(this);

                    Collection.Load(stream);
                }

                IsModified = false;
            }
        }

        internal class LinkWrapper
        {
            public Ptr Ptr;
            public Link Link;

            public LinkWrapper(Ptr ptr, Link link)
            {
                Ptr = ptr;
                Link = link;
            }

            public LinkWrapper()
                : this(Ptr.NULL, null)
            {
            }
        }
    }
}
